#include "Terrain/TerrainUpdater.h"
#include "GameSingleton.h"
#include "ServerConfigure.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemWeather.h"
#include "Subsystems/SubsystemPlayers.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/BlockBehaviors/SubsystemBlockBehaviors.h"
#include "Managers/GameManager.h"
#include "Managers/BlocksManager.h"

#include "Components/ComponentPlayer.h"
#include "Components/ComponentBody.h"

#include "Player/PlayerData.h"
#include "Log.h"
#include "GameThreadPool.hpp"

namespace PocketSurvival
{
    TerrainUpdater::TerrainUpdater()
    {
        playersRoundCheckFinish = true;
        m_subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        m_subsystemWeather = GameSingleton::singletonPtr->getSingleton<SubsystemWeather>();
        m_subsystemPlayers = GameSingleton::singletonPtr->getSingleton<SubsystemPlayers>();
        m_subsystemTimeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        m_subsystemBlockBehaviors = GameSingleton::singletonPtr->getSingleton<SubsystemBlockBehaviors>();
    }

    TerrainUpdater::~TerrainUpdater() {}

    void TerrainUpdater::playersRoundCheck()
    {
        std::set<Point2> playerChunkCoordsSet;

        std::vector<std::shared_ptr<PlayerData>> playerVec;
        m_subsystemPlayers->getAllPlayerData(playerVec);
        int32_t radius = GameSingleton::serverConfigure->playerChunkRadius;
        for(auto it = playerVec.begin(); it != playerVec.end(); ++it)
        {
            Vector3 position;
            (*it)->componentPlayer->componentBody->getPosition(position);

            int32_t coordX = (int32_t)position.posX >> 4;
            int32_t coordY = (int32_t)position.posZ >> 4;
            for(int32_t x = -radius; x <= radius; x++)
            {
                for(int32_t y = -radius; y <= radius; y++)
                {
                    playerChunkCoordsSet.emplace(coordX + x, coordY + y);
                }
            }
        }

        for(auto it = playerChunkCoordsSet.begin(); it != playerChunkCoordsSet.end(); ++it)
        {
            TerrainChunk *terrainChunk = m_subsystemTerrain->getTerrainChunk(it->posX, it->posY);
            if(terrainChunk != nullptr)
            {
                terrainChunk->recycleTime = 0.0f;
            }
            else
            {
                m_subsystemTerrain->loadTerrrainChunk(it->posX, it->posY);
            }
        }
        playersRoundCheckFinish = true;
    }

    void TerrainUpdater::update(float dt)
    {
        // 检测并加载玩家周边的区块
        if (playersRoundCheckFinish == true)
        {
            playersRoundCheckFinish = false;
            GameSingleton::gameNoWaitThreadPool->push_task(&TerrainUpdater::playersRoundCheck, this);
        }


        if(m_lastSkylightValue != m_subsystemWeather->skylightValue)
        {
            m_lastSkylightValue = m_subsystemWeather->skylightValue;
            m_isResetSkylightValue = true;
        }

        std::vector<TerrainChunk*> chunkVec;
        m_subsystemTerrain->getAllTerrainChunk(chunkVec);

        auto *blockBehaviorsByContents = m_subsystemBlockBehaviors->getBlockBehaviorsByContents();

        for (TerrainChunk *terrainChunk : chunkVec)
        {
            if (terrainChunk->State >= TerrainChunkState::InvalidLight)
            {
                // 这里不需要判断remove,因为再此update调用之前，已经将需要remove的区块都回收掉了
                if(terrainChunk->recycleTime >= GameSingleton::serverConfigure->terrainChunkRecycleTime)
                {
                    for(int32_t num = 0; num < 1024; ++num)
                    {
                        for(auto &behavior : blockBehaviorsByContents[num])
                        {
                            behavior->onChunkDiscarding(terrainChunk);
                        }
                    }

                    terrainChunk->isRemove = true;
                    continue;
                }
                terrainChunk->recycleTime += dt;
            }
        }

        if(m_subsystemTimeOfDay->periodicGameTimeEvent(3.5, 0.0))
        {
            bool isResetSkylightValue = m_isResetSkylightValue;
            m_isResetSkylightValue = false;

            for(auto it = chunkVec.begin(); it != chunkVec.end(); ++it)
            {
                TerrainChunk *terrainChunk = *it;
                if(terrainChunk->State >= TerrainChunkState::InvalidLight)
                {
                    if(isResetSkylightValue == true)
                    {
                        terrainChunk->State = TerrainChunkState::InvalidLight;
                    }
                    if (terrainChunk->State < TerrainChunkState::Valid)
                    {
                        m_waitUpdateSingleStepCoordSet.insert(terrainChunk->coords);
                        // updateChunkSingleStep(terrainChunk, m_subsystemWeather->skylightValue);
                    }
                }
            }
        }

        // 光照加载用的是单线程加载，所以一次性不能加载过多，不然会卡到tick
        if (m_waitUpdateSingleStepCoordSet.size() > 0)
        {
            Point2 coords = *(m_waitUpdateSingleStepCoordSet.begin());
            m_waitUpdateSingleStepCoordSet.erase(m_waitUpdateSingleStepCoordSet.begin());
            TerrainChunk *chunk = m_subsystemTerrain->getTerrainChunk(coords.posX, coords.posY);
            if (chunk != nullptr)
            {
                while(chunk->State < TerrainChunkState::Valid)
                {
                    updateChunkSingleStep(chunk, m_subsystemWeather->skylightValue);
                }
            }
        }
    }

    void TerrainUpdater::generateChunkSunLightAndHeight(TerrainChunk *chunk, int32_t skylightValue)
    {
        for (int32_t i = 0; i < 16; i++)
        {
            for (int32_t j = 0; j < 16; j++)
            {
                int32_t num = 0;
                int32_t num2 = 255;
                int32_t num3 = 0;
                int32_t num4 = 255;
                int32_t num5 = TerrainChunk::CalculateCellIndex(i, 255, j);
                while (num4 >= 0)
                {
                    int32_t cellValueFast = chunk->getCellValueFast(num5);
                    if (Terrain::ExtractContents(cellValueFast) != 0)
                    {
                        num = num4;
                        break;
                    }
                    cellValueFast = Terrain::ReplaceLight(cellValueFast, skylightValue);
                    chunk->setCellValueFast(num5, cellValueFast);
                    num4--;
                    num5--;
                }
                num4 = 0;
                num5 = TerrainChunk::CalculateCellIndex(i, 0, j);
                while (num4 <= num + 1)
                {
                    int32_t cellValueFast2 = chunk->getCellValueFast(num5);
                    int32_t num6 = Terrain::ExtractContents(cellValueFast2);
                    if (GameSingleton::blocksManager->blocks[num6]->isTransparent(cellValueFast2))
                    {
                        num2 = num4;
                        break;
                    }
                    cellValueFast2 = Terrain::ReplaceLight(cellValueFast2, 0);
                    chunk->setCellValueFast(num5, cellValueFast2);
                    num4++;
                    num5++;
                }
                int32_t num7 = skylightValue;
                num4 = num;
                num5 = TerrainChunk::CalculateCellIndex(i, num, j);
                if (num7 > 0)
                {
                    while (num4 >= num2)
                    {
                        int32_t cellValueFast3 = chunk->getCellValueFast(num5);
                        int32_t num8 = Terrain::ExtractContents(cellValueFast3);
                        if (num8 != 0)
                        {
                            Block *block = GameSingleton::blocksManager->blocks[num8];
                            if (false == block->isTransparent(cellValueFast3) || block->LightAttenuation >= num7)
                            {
                                break;
                            }
                            num7 -= block->LightAttenuation;
                        }
                        cellValueFast3 = Terrain::ReplaceLight(cellValueFast3, num7);
                        chunk->setCellValueFast(num5, cellValueFast3);
                        num4--;
                        num5--;
                    }
                }
                num3 = num4 + 1;
                while (num4 >= num2)
                {
                    int32_t cellValueFast4 = chunk->getCellValueFast(num5);
                    cellValueFast4 = Terrain::ReplaceLight(cellValueFast4, 0);
                    chunk->setCellValueFast(num5, cellValueFast4);
                    num4--;
                    num5--;
                }
                chunk->setTopHeightFast(i, j, num);
                chunk->setBottomHeightFast(i, j, num2);
                chunk->setSunlightHeightFast(i, j, num3);
            }
        }
    }

    void TerrainUpdater::generateChunkLightSources(TerrainChunk *chunk, std::vector<LightSource> &lightSourceVec)
    {
        for (int32_t i = 0; i < 16; i++)
        {
            for (int32_t j = 0; j < 16; j++)
            {
                int32_t num  = i + chunk->origin.posX;
                int32_t num2 = j + chunk->origin.posY;
                int32_t topHeightFast    = chunk->getTopHeightFast(i, j);
                int32_t bottomHeightFast = chunk->getBottomHeightFast(i, j);
                int32_t num4 = bottomHeightFast;
                int32_t num5 = TerrainChunk::CalculateCellIndex(i, bottomHeightFast, j);
                while(num4 <= topHeightFast)
                {
                    int32_t cellValueFast = chunk->getCellValueFast(num5);
                    Block *block = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(cellValueFast)];
                    int32_t num6 = 0;
                    if (block->DefaultEmittedLightAmount > 0)
                    {
                        num6 = block->getEmittedLightAmount(cellValueFast);
                    }
                    if(num6 > 0)
                    {
                        chunk->setCellValueFast(num5, Terrain::ReplaceLight(cellValueFast, num6));
                        lightSourceVec.emplace_back(num, num4, num2, num6);
                    }
                    num4++;
                    num5++;
                }
            }
        }
    }

    // 光照扩散计算
    void TerrainUpdater::propagateLight(TerrainChunk *chunk, std::vector<LightSource> &lightSourceVec)
    {
        static const Point3 propagatePoint[] = {
            Point3(-1, 0, 0),
            Point3(0, -1, 0),
            Point3(0, 0, -1),
            Point3(1, 0, 0),
            Point3(0, 1, 0),
            Point3(0, 0, 1)
        };

        std::vector<LightSource> lightSourceVecCatch;
        lightSourceVecCatch.reserve(5000);
        TerrainChunk *terrainChunkArray[9] { nullptr };
        Point2 startPoint(chunk->coords.posX - 1, chunk->coords.posY - 1);

        for(int32_t x = 0; x < 3; ++x)
        {
            for(int32_t y = 0; y < 3; ++y)
            {
                TerrainChunk *terrainChunk = m_subsystemTerrain->getTerrainChunk(startPoint.posX + x, startPoint.posY + y);
                terrainChunkArray[y * 3 + x] = terrainChunk;
            }
        }
        
        while(lightSourceVec.size() > 0)
        {
            for(auto it = lightSourceVec.cbegin(); it != lightSourceVec.cend(); ++it)
            {
                for(int32_t index = 0; index < (sizeof(propagatePoint) / sizeof(propagatePoint[0])); ++index)
                {
                    Point3 thePoint = it->point + propagatePoint[index];
                    int32_t coordX = ((thePoint.posX) >> 4) - startPoint.posX;
                    int32_t coordY = ((thePoint.posZ) >> 4) - startPoint.posY;
                    TerrainChunk* terrainChunk = terrainChunkArray[coordY * 3 + coordX];
                    if(terrainChunk != nullptr && thePoint.posY < 255 && thePoint.posY > 0)
                    {
                        int32_t blockIndex = TerrainChunk::CalculateCellIndex(thePoint.posX & 0xF, thePoint.posY, thePoint.posZ & 0xF);

                        terrainChunk->cellValueLock.lock();

                        int32_t cellValue = terrainChunk->getCellValueFastNoLock(blockIndex);
                        Block *block = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(cellValue)];
                        if(block->IsTransparent)
                        {
                            int32_t num2 = it->light - block->LightAttenuation - 1;
                            int32_t light = Terrain::ExtractLight(cellValue);
                            if(num2 > light)
                            {
                                if(terrainChunk == chunk)
                                {
                                    lightSourceVecCatch.emplace_back(thePoint.posX, thePoint.posY, thePoint.posZ, num2);
                                }
                                terrainChunk->setCellValueFastNoLock(blockIndex, Terrain::ReplaceLight(cellValue, num2));
                            }
                        }

                        terrainChunk->cellValueLock.unlock();
                    }
                }
            }
            // lightSourceVec.clear();
            lightSourceVec.swap(lightSourceVecCatch);
            lightSourceVecCatch.clear();
        }
    }

    void TerrainUpdater::updateChunkSingleStep(TerrainChunk *chunk, int32_t skylightValue)
    {
        switch (chunk->State)
        {
        case TerrainChunkState::NotLoaded:
            if (m_subsystemTerrain->terrainSerializer->loadChunk(chunk) == true)
            {
                chunk->State = TerrainChunkState::InvalidLight;
            }
            else
            {
                chunk->State = TerrainChunkState::InvalidContents1;
            }
            break;
        case TerrainChunkState::InvalidContents1:
            m_subsystemTerrain->terrainContentsGenerator->generateChunkContentsPass1(chunk);
            chunk->State = TerrainChunkState::InvalidContents2;
            break;
        case TerrainChunkState::InvalidContents2:
            m_subsystemTerrain->terrainContentsGenerator->generateChunkContentsPass2(chunk);
            chunk->State = TerrainChunkState::InvalidContents3;
            break;
        case TerrainChunkState::InvalidContents3:
            m_subsystemTerrain->terrainContentsGenerator->generateChunkContentsPass3(chunk);
            chunk->State = TerrainChunkState::InvalidContents4;
            break;
        case TerrainChunkState::InvalidContents4:
            m_subsystemTerrain->terrainContentsGenerator->generateChunkContentsPass4(chunk);
            chunk->State = TerrainChunkState::InvalidLight;
            break;
        case TerrainChunkState::InvalidLight:
            generateChunkSunLightAndHeight(chunk, skylightValue);
            chunk->State = TerrainChunkState::InvalidPropagatedLight;
            break;
        case TerrainChunkState::InvalidPropagatedLight:
            {
                std::vector<LightSource> lightSourceVec;
                generateChunkLightSources(chunk, lightSourceVec);
                propagateLight(chunk, lightSourceVec);
            }
            chunk->State = TerrainChunkState::Valid;
            break;
        case TerrainChunkState::InvalidVertices1:
        case TerrainChunkState::InvalidVertices2:
            chunk->State = TerrainChunkState::Valid;
            break;
        }
    }

    void TerrainUpdater::loadChunk(TerrainChunk *chunk)
    {
        if(m_subsystemTerrain->terrainSerializer->loadChunk(chunk) == false)
        {
            m_subsystemTerrain->terrainContentsGenerator->generateChunkContentsPass1(chunk);
            m_subsystemTerrain->terrainContentsGenerator->generateChunkContentsPass2(chunk);
            m_subsystemTerrain->terrainContentsGenerator->generateChunkContentsPass3(chunk);
            m_subsystemTerrain->terrainContentsGenerator->generateChunkContentsPass4(chunk);
        }
        chunk->State = TerrainChunkState::InvalidLight;

    }
    
}
